/*
 *  Copyright 2011 yura.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package org.opu.db_vdumper.ui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;

import net.infonode.docking.DockingWindow;
import net.infonode.docking.DockingWindowAdapter;
import net.infonode.docking.OperationAbortedException;
import net.infonode.docking.RootWindow;
import net.infonode.docking.SplitWindow;
import net.infonode.docking.TabWindow;
import net.infonode.docking.View;
import net.infonode.docking.ViewSerializer;
import net.infonode.docking.WindowBar;
import net.infonode.docking.properties.RootWindowProperties;
import net.infonode.docking.theme.DockingWindowsTheme;
import net.infonode.docking.util.DockingUtil;
import net.infonode.docking.util.MixedViewHandler;
import net.infonode.docking.util.ViewMap;
import net.infonode.util.Direction;
import org.opu.db_vdumper.actions.db.DbAction;

import org.opu.db_vdumper.io.FileIO;
import org.opu.db_vdumper.resources.ConfigPath;
import org.opu.db_vdumper.resources.JProperties;
import org.opu.db_vdumper.resources.ResourceHandle;
import org.opu.db_vdumper.ui.ThemeManager.ThemeException;
import org.opu.db_vdumper.ui.component.PropertiesPanel;
import org.opu.db_vdumper.ui.component.area.Canves;
import org.opu.db_vdumper.ui.component.area.model.CanvesModel;
import org.opu.db_vdumper.ui.component.sql.SqlEditPane;
import org.opu.db_vdumper.ui.component.tree.TreePanel;
import org.opu.db_vdumper.ui.i18n.DbVDumperViewI18N;
import org.opu.db_vdumper.ui.i18n.DbVDumperViewI18NImpl;
import org.opu.db_vdumper.util.Logger;

/**
 *
 * @author yura
 */
public class DbVDumperView extends JFrame implements MainView{

    private static final long serialVersionUID = 973711332096920599L;
	
    public static final String LOCATION = "location";
    public static final String MAXIMIZE = "maximize";
    public static final String SIZE = "size";

    public static final String FREEZE_LAYOUT = "freeze-layout";
    public static final String CLOSE_BUTTON_ENABLE = "close-button-enable";
    public static final String THEME = "theme";
    public static final String LAF = "laf";

    /** Properties map with all properties of window. */
    private JProperties configs;

    /** Internationalization text */
    private DbVDumperViewI18N i18n;

    /** In this properties object the modified property values for close  buttons
     * etc. are stored. This object is cleared when the theme is changed. */
    private RootWindowProperties properties;

    /** The currently applied docking windows theme */
    private DockingWindowsTheme currentTheme;

    /** The one and only root window */
    private RootWindow rootWindow;

    /** Contains all the static views */
    private ViewMap viewMap;

    /** An array of the static views: tree, graph, sql-editor */
    private Map<String, View> views;

    /** Main tab container */
    private TabWindow tabWindow = new TabWindow();

    private final Canves canves;
    private final TreePanel tree;
    private final SqlEditPane sql;
    private final PropertiesPanel propPanel;

    public DbVDumperView(JProperties prop) {
        Logger.getInstance().debug(DbVDumperView.class, "Start creating main view");

        views = new HashMap<String, View>();
        properties = new RootWindowProperties();
        currentTheme = ThemeManager.defaultTheme();
        viewMap = new ViewMap();
        
        i18n = new DbVDumperViewI18NImpl();
        canves = new Canves(null);
        tree = new TreePanel(null);
        sql = new SqlEditPane(tabWindow);
        propPanel = new PropertiesPanel();
        
        Logger.getInstance().debug(DbVDumperView.class, "Init final variable");

        initFrame(prop);
        Logger.getInstance().debug(DbVDumperView.class, "Init frame options");

        createRootWindow();
        Logger.getInstance().info(DbVDumperView.class, "Create main view");
    }

    /////////////////////////////////////////////
    /////////// Seciton connect to frame
    /////////////////////////////////////////////

    private void initFrame(JProperties prop) {
        if (prop == null) {
            configs = new JProperties();
            try {
                FileIO.load(ConfigPath.getInstance().getFrameProp(), configs);
            } catch (IOException ex) {
                Logger.getInstance().info(DbVDumperView.class,
                        "When load properties - " + ex);
            }
        } else {
            configs = prop;
        }

        setSize(configs.getDimension(SIZE, new Dimension(700, 560)));
        Point point = configs.getPoint(LOCATION);
        if (point != null) {
            setLocation(point);
        } else {
            setLocationRelativeTo(null);
        }
        try {
            currentTheme = ThemeManager.getTheme(configs.getProperty(THEME));
        } catch (ThemeException ex) {
            Logger.getInstance().error(DbVDumperView.class, ex);
        }

        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        setTitle(ResourceHandle.getApplicationTitle());
    }

    private void add(String name, String title, JComponent comp){
        View vw = new View(title, ResourceHandle.getIcon(name + ".png"), comp);
        views.put(name, vw);
        viewMap.addView(name.hashCode(), vw);
    }

    /**
     * Creates the root window and the views.
     */
    private void createRootWindow() {
        // Init views.
        add(TREE_TABLE_SCEMA_VIEW, i18n.getTreeTitle(), compInScrol(tree));
        add(GRAPH_TABLE_SCEMA_VIEW, i18n.getGraphTitle(), compInScrol(canves));
        add(SQL_QUERY_EDITOR_VIEW, i18n.getEditorTitle(), sql);
        add(DB_CONNECTION_PROP_VIEW, i18n.getDbPropTitle(), compInScrol(propPanel));

        MixedViewHandler handler = new MixedViewHandler(viewMap, new ViewSerializer() {

            @Override
            public void writeView(View view, ObjectOutputStream out) throws IOException {
//                out.writeInt(((DynamicView) view).getId());
                throw new UnsupportedOperationException("Not supported yet.");
            }

            @Override
            public View readView(ObjectInputStream in) throws IOException {
//                return getDynamicView(in.readInt());
                throw new UnsupportedOperationException("Not supported yet.");
            }
        });

        rootWindow = DockingUtil.createRootWindow(viewMap, handler, true);

        // Set gradient theme. The theme properties object is the super object
        // of our properties object, which means our property value settings
        // will override the theme values
        properties.addSuperObject(currentTheme.getRootWindowProperties());

        // Our properties object is the super object of the root window
        // properties object, so all property values of the theme and in our
        // property object will be used by the root window
        rootWindow.getRootWindowProperties().addSuperObject(properties);

        // Enable window bar
        initBar(Direction.DOWN);
        initBar(Direction.LEFT);
        initBar(Direction.RIGHT);
        initBar(Direction.UP);

        setFreezeLayout(configs.getBoolean(FREEZE_LAYOUT, false));

        // Add a listener which shows dialogs when a window is closing or closed.
        rootWindow.addListener(new DockingWindowAdapter() {

            @Override
            public void windowAdded(DockingWindow to, DockingWindow window) {
                updateViews(window, true);
            }

            @Override
            public void windowRemoved(DockingWindow from, DockingWindow window) {
                updateViews(window, false);
            }

            @Override
            public void windowClosing(DockingWindow window) throws OperationAbortedException {
            }
        });

        tabWindow.addTab(views.get(TREE_TABLE_SCEMA_VIEW));
        tabWindow.addTab(views.get(DB_CONNECTION_PROP_VIEW));
        tabWindow.addTab(views.get(GRAPH_TABLE_SCEMA_VIEW));
        tabWindow.addTab(views.get(SQL_QUERY_EDITOR_VIEW));

        rootWindow.setWindow(new SplitWindow(true,//horizontal
                                0.25f,
                                new SplitWindow(false,//vertical
                                    0.7f,
                                    views.get(TREE_TABLE_SCEMA_VIEW),
                                    views.get(DB_CONNECTION_PROP_VIEW)),
                                new SplitWindow(false,//vertical
                                    0.7f,
                                    tabWindow,
                                    views.get(SQL_QUERY_EDITOR_VIEW))));

        WindowBar windowBar = rootWindow.getWindowBar(Direction.DOWN);
        while (windowBar.getChildWindowCount() > 0) {
            windowBar.getChildWindow(0).close();
        }
        
        windowBar.addTab(views.get(DB_CONNECTION_PROP_VIEW));
        windowBar.addTab(views.get(SQL_QUERY_EDITOR_VIEW));

        getContentPane().add(rootWindow);
    }

    private void initBar(Direction direction){
        boolean val = configs.getBoolean(direction.getName(), true);
        rootWindow.getWindowBar(direction).setEnabled(val);
    }

    private JComponent compInScrol(JComponent comp){
        JScrollPane p = new JScrollPane(comp);
        p.getViewport().setBackground(Color.WHITE);
        return p;
    }


    /////////////////////////////////////////////
    /////////// Seciton connect to idw-docking
    /////////////////////////////////////////////


    @Override
    public void reset() {
        this.repaint();
    }

    @Override
    public void setTheme(DockingWindowsTheme theme) {
        // If theme is same as already - do nothing
        if (theme.getClass() == currentTheme.getClass()){
            return ;
        }

        // Clear the modified properties values
        properties.getMap().clear(true);

        // set theme
        RootWindowProperties rootW = currentTheme.getRootWindowProperties();
        properties.replaceSuperObject(rootW, theme.getRootWindowProperties());
        
        // store it
        currentTheme = theme;
    }

    @Override
    public DockingWindowsTheme getTheme(){
        return currentTheme;
    }

    @Override
    public void setToogleEnabled(Direction direction, boolean enable) {
        rootWindow.getWindowBar(direction).setEnabled(enable);
        configs.setBoolean(direction.getName(), enable);
        Logger.getInstance().debug(this, "Set toogle " + direction.getName()
                + "  Enabled " + enable);
    }

    @Override
    public boolean isToogleEnabled(Direction direction){
        return rootWindow.getWindowBar(direction).isEnabled();
    }

    @Override
    public void replaceToogleEnabled(Direction direction) {
        boolean enable = !rootWindow.getWindowBar(direction).isEnabled();
        setToogleEnabled(direction, enable);
    }

    /**
     * Update view menu items and dynamic view map.
     *
     * @param window the window in which to search for views
     * @param added  if true the window was added
     */
    @Override
    public void updateViews(DockingWindow window, boolean added) {
        if (window instanceof View) {
            // TODO: do something.

        } else {
            for (int i = 0; i < window.getChildWindowCount(); i++) {
                updateViews(window.getChildWindow(i), added);
            }
        }
    }

    /////////////////////////////////////////////
    /////////// New API. of current component.
    /////////////////////////////////////////////

    @Override
    public void setFreezeLayout(boolean freeze) {
        configs.setBoolean(FREEZE_LAYOUT, freeze);

        // Disable window operations
        properties.getDockingWindowProperties().setDragEnabled(!freeze);
        properties.getDockingWindowProperties().setCloseEnabled(!freeze);
        properties.getDockingWindowProperties().setMinimizeEnabled(!freeze);
        properties.getDockingWindowProperties().setRestoreEnabled(!freeze);
        properties.getDockingWindowProperties().setMaximizeEnabled(!freeze);

        // Enable tab reordering inside tabbed panel
        properties.getTabWindowProperties().getTabbedPanelProperties()
                .setTabReorderEnabled(freeze);
    }

    @Override
    public boolean isFreezeLayout(){
        return properties.getTabWindowProperties().getTabbedPanelProperties()
                .getTabReorderEnabled();
    }

    @Override
    public void setCloseButtonEnable(boolean enable){
        configs.setBoolean(CLOSE_BUTTON_ENABLE, enable);

        properties.getDockingWindowProperties().setCloseEnabled(true).getCloseEnabled();
    }

    @Override
    public boolean isCloseButtonEnable(){
        return properties.getDockingWindowProperties().getCloseEnabled();
    }

    @Override
    public void setCanvesModel(CanvesModel model) {
        Logger.getInstance().debug(this, "Change canves model");
        tree.setViewModel(model);
        canves.setModel(model);
        model.addViews(canves);
        model.addViews(tree);
    }

    @Override
    public void setDbProp(Map<String, String> properties) {
        propPanel.setProperties(properties);
    }

    public void setLaF(String value){
        configs.setProperty(LAF, value);
    }

    @Override
    public void restore(String viewId) {
        View view = views.get(viewId);

        if (view != null) {
            if (view.getRootWindow() != null) {
                view.restoreFocus();
            } else {
                DockingUtil.addWindow(view, rootWindow);
            }
        } else {
            Logger.getInstance().warning(this, "Now view " + viewId);
        }
    }

    public List<ActionListener> getFinalActions() {
        List<ActionListener> listeners = new ArrayList<ActionListener>();
        listeners.add(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                File file = ConfigPath.getInstance().getFrameProp();

                configs.setPoint(LOCATION, getLocation());
                configs.setDimension(SIZE, getSize());
                configs.setBoolean(MAXIMIZE, isMaximumSizeSet());

                try {
                    configs.setProperty(THEME, ThemeManager.getName(currentTheme));
                } catch (ThemeException ex) {
                    Logger.getInstance().error(DbVDumperView.class, ex);
                }

                try {
                    FileIO.store(file, configs, "window");
                } catch (IOException ex) {
                    Logger.getInstance().error(DbVDumperView.class,
                            "When store properties - " + ex);
                }
            }
        });
        return listeners;
    }

    @Override
    public JFrame asFrame() {
        return this;
    }

    @Override
    public TextResource getTextResource() {
        return sql;
    }

    @Override
    public void setDbActions(Collection<DbAction> actions) {
        sql.setDbActions(actions);
    }
}
